package cn.bossfriday.common.utils;

import cn.bossfriday.common.combo.Combo2;
import org.apache.commons.collections4.CollectionUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * SocketUtils
 *
 * @author chenx
 */
public class SocketUtils {

    private static final int MIN_CONNECTION_TIMEOUT = 3000;

    private SocketUtils() {
        // just do nothing
    }

    /**
     * selectFastestAddress
     *
     * @param addressList
     * @return
     */
    public static InetSocketAddress selectFastestAddress(List<InetSocketAddress> addressList, int connectionTimeout) {
        if (CollectionUtils.isEmpty(addressList)) {
            return null;
        }

        if (connectionTimeout <= MIN_CONNECTION_TIMEOUT) {
            connectionTimeout = MIN_CONNECTION_TIMEOUT;
        }

        List<Combo2<InetSocketAddress, Long>> testResultList = new ArrayList<>();
        for (InetSocketAddress address : addressList) {
            try {
                testResultList.add(testConnection(address, connectionTimeout));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        // 按照连接时间排序
        Collections.sort(testResultList, Comparator.comparing(Combo2::getV2));

        if (CollectionUtils.isEmpty(testResultList)) {
            return null;
        }

        return testResultList.get(0).getV1();
    }

    /**
     * testConnection
     *
     * @param address
     * @param connectionTimeout
     * @return
     * @throws IOException
     */
    private static Combo2<InetSocketAddress, Long> testConnection(InetSocketAddress address, int connectionTimeout) throws IOException {
        try (Socket socket = new Socket()) {
            long startTime = System.currentTimeMillis();
            socket.connect(address, connectionTimeout);
            long time = System.currentTimeMillis() - startTime;

            return new Combo2<>(address, time);
        }
    }
}
